/*
 * Driver -mac80211 core- for Winbond USB2WLAN b/g WiFi device on Linux 2.6
 * Many portions based on original GPLed driver from Winbond.
 *
 * Written by Ferenc Csicsova & Karoly Kasza
 * Based on RTL8187 driver in kernel.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/usb.h>
#include <net/mac80211.h>
#include <linux/etherdevice.h>

#include "wbusb.h"
#include "wbusb_airoha.h"

static struct usb_device_id Id_Table[] = {
  {USB_DEVICE( 0x0416, 0x0035 )},
  {USB_DEVICE( 0x18E8, 0x6201 )},
//Tested on the device below, 0x18e8-0x6206
  {USB_DEVICE( 0x18E8, 0x6206 )},
  {USB_DEVICE( 0x18E8, 0x6217 )},
  {USB_DEVICE( 0x18E8, 0x6230 )},
  {USB_DEVICE( 0x18E8, 0x6233 )},
  {USB_DEVICE( 0x1131, 0x2035 )},
  { }
};

MODULE_DEVICE_TABLE(usb, Id_Table);
MODULE_AUTHOR("Ferenc Csicsova");
MODULE_AUTHOR("Karoly Kasza");
MODULE_DESCRIPTION("Winbond USB2WLAN b/g WiFi driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");

//--------------------------------------------------------------------
//Read 4 bytes from eeprom
u32 wbusb_read_eeprom(struct wbusb_priv *priv, u8 addr)
{
	u32 ret;

	wbusb_reg_write_sync(priv, REG_EEPROM, 0x08000000 | (addr << 16) );
	wbusb_reg_read_sync(priv, REG_EEPROM, &ret);

	return ret;
}

//Get MAC from EEPROM
void wbusb_get_mac(struct wbusb_priv *priv, u8 *perm_addr)
{
	u32 macaddr;
	int i;

	for (i=0; i<3; i++) {
		macaddr = wbusb_read_eeprom(priv, EEPROM_MAC+i);
		perm_addr[i*2] = cpu_to_le16((u16)macaddr);;
		perm_addr[i*2+1] = cpu_to_be16((u16)macaddr);
	}

	DEBUG("get_mac: (%02x:%02x:%02x:%02x:%02x:%02x)", perm_addr[0], perm_addr[1], perm_addr[2], perm_addr[3], perm_addr[4], perm_addr[5])

	return;
}

//Set MAC for chip
void wbusb_set_mac(struct wbusb_priv *priv, u8 *perm_addr)
{
	//Give the mac back to the chip
	wbusb_reg_write_sync(priv, REG_U+0x1e8, cpu_to_le32(priv->perm_addr_high));
	wbusb_reg_write_sync(priv, REG_U+0x1ec, cpu_to_le32(priv->perm_addr_low));

	DEBUG("set_mac: (%02x:%02x:%02x:%02x:%02x:%02x)", perm_addr[0], perm_addr[1], perm_addr[2], perm_addr[3], perm_addr[4], perm_addr[5])
	return;
}

//--------------------------------------------------------------------
//Initialize RF chip
static int rf_init(struct wbusb_priv *priv)
{
	u32 type = wbusb_read_eeprom(priv, EEPROM_RFCHIP);

	priv->radio.type = (u8)(type & 0xff);
	priv->radio.name = NULL;
	DEBUG("rf_init type: %i", priv->radio.type)

	//Start calling inits, hopefully one of them will find its controller
	airoha_struct_init(priv);
	//Others will come here

	if (priv->radio.name==NULL) {
		printk(KERN_ERR "wbusb: Unknown RF chip (%d)\n", priv->radio.type);
		printk(KERN_ERR "wbusb: Please report this ID to the developers!\n");
		return 1;
	} else printk("wbusb: RF chip type: %s\n", priv->radio.name);

	return 0;
}

//--------------------------------------------------------------------

//FROM OLD CODE!!!

//==================================================================
// BitReverse --
//   Reverse the bits in the input argument, dwData, which is 
//   regarded as a string of bits with the length, DataLength.
// 
// Arguments:
//   dwData     :
//   DataLength :
//
// Return:
//   The converted value.
//==================================================================
u32 BitReverse( u32 dwData, u32 DataLength)
{
	u32   HalfLength, i, j;
	u32   BitA, BitB;

	if ( DataLength <= 0)       return 0;   // No conversion is done.
	dwData = dwData & (0xffffffff >> (32 - DataLength));

	HalfLength = DataLength / 2;
	for ( i = 0, j = DataLength-1 ; i < HalfLength; i++, j--)
	{
		BitA = GetBit( dwData, i);
		BitB = GetBit( dwData, j);
		if (BitA && !BitB) {
			dwData = ClearBit( dwData, i);
			dwData = SetBit( dwData, j);
		} else if (!BitA && BitB) {
			dwData = SetBit( dwData, i);
			dwData = ClearBit( dwData, j);
		} else
		{
			// Do nothing since these two bits are of the save values.
		}
	}

	return dwData;
}

//--------------------------------------------------------------------
//Send data
static int wbusb_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
{
	DEBUG("tx")
	return 0;
}

/*
//When a packet flies in...
static void wbusb_rx_cb(struct urb *urb)
{
	struct sk_buff *skb = (struct sk_buff *)urb->context;
	struct wbusb_rx_info *info = (struct wbusb_rx_info *)skb->cb;
	struct ieee80211_hw *dev = info->dev;
	struct wbusb_priv *priv = dev->priv;
	struct wbusb_rx_hdr *hdr;
	struct ieee80211_rx_status rx_status = { 0 };
//	int rate, signal;
	u32 flags;

	DEBUG("rx_cb")
	//Should be done by only 1 CPU at a time
	spin_lock(&priv->rx_queue.lock);
	if (skb->next)
		__skb_unlink(skb, &priv->rx_queue);
	else {
		spin_unlock(&priv->rx_queue.lock);
		return;
	}
	spin_unlock(&priv->rx_queue.lock);

	//It is unlikely that unlikely() is a sane macro...
	if (unlikely(urb->status)) {
		usb_free_urb(urb);
		dev_kfree_skb_irq(skb);
		return;
	}

	skb_put(skb, urb->actual_length);
	hdr = (struct wbusb_rx_hdr *)(skb_tail_pointer(skb) - sizeof(*hdr));
	flags = le32_to_cpu(hdr->flags);
	skb_trim(skb, flags & 0x0FFF);

	//TO BE CONTINUED...
	//device specific code here
	//This side up!
	//Fragile!

	ieee80211_rx_irqsafe(dev, skb, &rx_status);

	skb = dev_alloc_skb(WBUSB_MAX_RX);
	if (unlikely(!skb)) {
		usb_free_urb(urb);
		// TODO check rx queue length and refill *somewhere*
		//The above comment is from rtl9197_dev.c :)
		return;
	}

	info = (struct wbusb_rx_info *)skb->cb;
	info->urb = urb;
	info->dev = dev;
	urb->transfer_buffer = skb_tail_pointer(skb);
	urb->context = skb;
	skb_queue_tail(&priv->rx_queue, skb);

	usb_submit_urb(urb, GFP_ATOMIC);

	return;
}
*/

//u8 wbusb_get_hw_radio_state(struct wbusb_priv *priv)
//{
//	wbusb_reg_read_sync(

//}

//Init hardware, like turn on antennas and such
static int wbusb_init_hw(struct ieee80211_hw *dev)
{
	struct wbusb_priv *priv = dev->priv;
	u32 tmp;

	DEBUG("init_hw call")

	//?? SPIN_LOCK needed?

	//Call the radio chip's starter here
	priv->radio.start(priv);

	//Reset hardware
	wbusb_reg_write_sync(priv, REG_U+0x01b0, 1);
	OS_SLEEP(10000);

	// Set burst write delay
	wbusb_reg_write_sync(priv, REG_U+0x01f8, 0x7ff);

	//VCO trim
	tmp=wbusb_read_eeprom(priv,EEPROM_VCO_TRIM);
	tmp&=0xff;
	if (tmp==0xff) tmp=0x28;
	priv->vco_trim = (u8)tmp;

	//Antenna on/off of software flag????
	tmp=wbusb_read_eeprom(priv,EEPROM_ANT);
	tmp&=0xffff;
	priv->sw_ant_set=(u16)tmp;

	//Read TXVGA
	tmp=wbusb_read_eeprom(priv,EEPROM_TXVGA);
	tmp&=0xff;
	priv->tx_vga=(u8)tmp;
	//TODO updated in (wb35reg_initial->GetTxVgaFromEEprom)

	//Scan interval, region
	tmp=wbusb_read_eeprom(priv,EEPROM_SC_INT);
	priv->region=(u8)tmp>>8;
	if ((priv->region<1) || (priv->region>6)) priv->region=0xff; //region auto???

	tmp&=0xff;
	if ((tmp==0xff) || (tmp==0)) tmp=SCAN_MAX_CH_TIME;
	priv->scan_int=(u16)(tmp*10);

	priv->radio.rf_synt_init(priv);

	priv->radio.bb_proc_init(priv);

//----Mxx Registers
	// M24 disable enter power save, BB RxOn and enable NAV attack
	wbusb_reg_write_sync(priv, REG_M+0x24, 0x08040042);

	// M28 no initialization required, but in the old driver it is set to 0
	wbusb_reg_write_sync(priv, REG_M+0x28, 0);

	// M2C CWmin and CWmax setting
	priv->cwmin = DEFAULT_CWMIN;
	priv->cwmax = DEFAULT_CWMAX;
	tmp=DEFAULT_CWMIN<<10 | DEFAULT_CWMAX;
	wbusb_reg_write_sync(priv, REG_M+0x2c, tmp);

	// M30 BSSID
//	tmp = *(u32*)priv->bssid;	//???? TODO
	wbusb_reg_write_sync(priv, REG_M+0x30, 0);

	// M34
	tmp=DEFAULT_AID<<16;
	//tmp|=*(u16*)(priv->bssid+4);		//???
	wbusb_reg_write_sync(priv, REG_M+0x34, tmp);

	// M38
	tmp = (DEFAULT_RATE_RETRY_LIMIT<<8) | (DEFAULT_LONG_RETRY_LIMIT << 4) | DEFAULT_SHORT_RETRY_LIMIT;
	wbusb_reg_write_sync(priv, REG_M+0x38, tmp);

	// M3C
	tmp = (DEFAULT_PIFST << 26) | (DEFAULT_EIFST << 16) | (DEFAULT_DIFST << 8) | (DEFAULT_SIFST << 4) | DEFAULT_OSIFST ;
	wbusb_reg_write_sync(priv, REG_M+0x3C, tmp);

	// M40
	priv->slot_time_select=DEFAULT_SLOT_TIME;;
	tmp = (DEFAULT_ATIMWD << 16) | DEFAULT_SLOT_TIME;
	wbusb_reg_write_sync(priv, REG_M+0x40, tmp);

	// M44
	tmp = DEFAULT_MAX_TX_MSDU_LIFE_TIME << 10; // *1024
	wbusb_reg_write_sync(priv, REG_M+0x44, tmp);

	// M48
	priv->beacon_period = DEFAULT_BEACON_INTERVAL;
	priv->probe_delay = DEFAULT_PROBE_DELAY_TIME;
	tmp = (DEFAULT_BEACON_INTERVAL << 16) | DEFAULT_PROBE_DELAY_TIME;
	wbusb_reg_write_sync(priv, REG_M+0x48, tmp);

	//M4C
	tmp = (DEFAULT_PROTOCOL_VERSION << 30) | (DEFAULT_MAC_POWER_STATE << 28) | (DEFAULT_DTIM_ALERT_TIME << 24);
	wbusb_reg_write_sync(priv, REG_M+0x4c, tmp);

	// M60
	wbusb_reg_write_sync(priv, REG_M+0x60, 0x12481248);

	// M68
	wbusb_reg_write_sync(priv, REG_M+0x68, 0x00050900);

	// M98
	wbusb_reg_write_sync(priv, REG_M+0x98, 0xffff8888);

//----Dxx Registers
	// Old IC:Single mode only.
	// New IC: operation decide by Software set bit[4]. 1:multiple 0: single
	wbusb_reg_write_sync(priv, REG_D+0x00, 0xc0000004); //Txon, Rxon, single Rx for new 4k DMA
	//TODO: set bit 4 for multiple rx mode, if everything ok

	DEBUG("init_hw end")
	return 0;
}

//Init URB for kernel @ add_interface
static int wbusb_init_urbs(struct ieee80211_hw *dev)
{
	struct wbusb_priv *priv = dev->priv;
//	struct urb *entry;
//	struct sk_buff *skb;
//	struct wbusb_rx_info *info;
	u32 pltmp[2];
	u8 ctmp[2];
	u32 rtmp;

	DEBUG("init_urbs")
	wbusb_rx_init(priv);
	wbusb_rx_start(priv);

//--------------------------------------------------------------------------
	//FULL PROMISC - TEST!!!
	DEBUG("FULL promisc enabled")
//	wbusb_reg_write_sync(priv, REG_M+0x00, 0x87400000);
	wbusb_reg_write_sync(priv, REG_M+0x00, 0x84000000);
	//bah
	wbusb_reg_write_sync(priv, REG_M+0x24, 0x8040042);

	wbusb_reg_read_sync(priv, 0x3b0, &rtmp);
	OS_SLEEP(100000);
	wbusb_reg_write_sync(priv, REG_M+0x64, 0x94040689);
	wbusb_reg_read_sync(priv, 0x3b0, &rtmp);


	wbusb_reg_write_sync(priv, REG_M+0x24, 0x8040042);
	wbusb_reg_write_sync(priv, REG_M+0x24, 0x8040042);

	pltmp[0] = 0x9403f790;
	pltmp[1] = 0x94033331;
	wbusb_reg_write_burst(priv,REG_M+0x64, pltmp, 2, NO_INC);

	wbusb_reg_write(priv, REG_M+0x64, 0x94040689);

	wbusb_reg_write(priv, REG_BB+0x50, 0x27106200);

	wbusb_reg_write(priv, REG_M+0x78, 0x0);
	wbusb_reg_write(priv, REG_BB+0x30, 0x2c54);

	wbusb_reg_write_wcbv(priv, REG_M+0x28, 0x9, ctmp, 2);
//--------------------------------------------------------------------------

/*
	//From rtl8187_dev.c, but why 8?
	while (skb_queue_len(&priv->rx_queue) < 8) {
		skb = __dev_alloc_skb(WBUSB_MAX_RX, GFP_KERNEL);
		if (!skb) {
			DEBUG("init_urbs: unable to dev_alloc_skb")
			break; //Y not return 1 ?!
		}
		entry = usb_alloc_urb(0, GFP_KERNEL);
		if (!entry) {
			kfree_skb(skb);
			DEBUG("init_urbs: unable to usb_alloc_usb")
			break;
		}
		//Set rx_cb function to handle incoming
		usb_fill_bulk_urb(entry, priv->udev,
				  usb_rcvbulkpipe(priv->udev, 1),
				  skb_tail_pointer(skb),
				  WBUSB_MAX_RX, wbusb_rx_cb, skb);
		info = (struct wbusb_rx_info *)skb->cb;
		info->urb = entry;
		info->dev = dev;
		skb_queue_tail(&priv->rx_queue, skb);
		usb_submit_urb(entry, GFP_KERNEL);
	}
*/
	return 0;
}

//Start device, first after ifconfig up
static int wbusb_start(struct ieee80211_hw *dev)
{
	int ret;

	DEBUG("start")
	ret = wbusb_init_hw(dev);
	return ret;
}

//ifconfig up, 2nd
static int wbusb_add_interface(struct ieee80211_hw *dev,
			       struct ieee80211_if_init_conf *conf)
{
	int ret;

	DEBUG("add_interface")
	ret = wbusb_init_urbs(dev); //No non-0 return yet
	return ret;
}

//ifconfig down, before stop
static void wbusb_remove_interface(struct ieee80211_hw *dev,
				     struct ieee80211_if_init_conf *conf)
{
	struct wbusb_priv *priv = dev->priv;
	priv->mode = NL80211_IFTYPE_MONITOR;
	priv->vif = NULL;
	DEBUG("remove_interface")
	return;
}

//Stop device, last
static void wbusb_stop(struct ieee80211_hw *dev)
{
	DEBUG("stop")
	return;
}

//called when iwlist scan
static int wbusb_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf)
{
	u16 chan = ieee80211_frequency_to_channel(conf->channel->center_freq);

	DEBUG("config")
	DEBUG("config: channel %d", chan)
	chan = 0; //So compiler won't cry :)
	return 0;
}

//When ifconfig up, before filter (flags)
static int wbusb_config_interface(struct ieee80211_hw *dev,
				  struct ieee80211_vif *vif,
				  struct ieee80211_if_conf *conf)
{
	DEBUG("config_interface")
	return 0;
}

//Set flags like PROMISC
static void wbusb_configure_filter(struct ieee80211_hw *dev,
				   unsigned int changed_flags,
				   unsigned int *total_flags,
				   int mc_count, struct dev_addr_list *mclist)
{
//	struct wbusb_priv *priv = dev->priv;

	DEBUG("configure_filter call -> (%02x)", *total_flags)

//Dunno if the following will ever be needed...
/*
//	promiscuous mode within your BSS, think of the BSS as your network segment and then this corresponds to the regular ethernet device promiscuous mode.
	if (changed_flags & FIF_PROMISC_IN_BSS) {
		DEBUG("configure_filter: FIF_PROMISC_IN_BSS")
	}

//	pass frames with failed FCS (but you need to set the RX_FLAG_FAILED_FCS_CRC for them)
	if (changed_flags & FIF_FCSFAIL) {
//		priv->rx_conf ^= RTL818X_RX_CONF_FCS;
		DEBUG("configure_filter: FIF_FCSFAIL")
	}

//	pass frames with failed PLCP CRC (but you need to set the RX_FLAG_FAILED_PLCP_CRC for them
	if (changed_flags & FIF_PLCPFAIL) {
		DEBUG("configure_filter: FIF_PLCPFAIL")
	}

//	This flag is set during scanning to indicate to the hardware that it should not filter beacons or probe responses by BSSID. Filtering them can greatly reduce the amount of processing mac80211 needs to do and the amount of CPU wakeups, so you should honour this flag if possible.
	if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
		DEBUG("configure_filter: FIF_BCN_PRBRESP_PROMISC")
	}

//	pass control frames, if PROMISC_IN_BSS is not set then only those addressed to this station
	if (changed_flags & FIF_CONTROL) {
		DEBUG("configure_filter: FIF_CONTROL")
	}

//	pass frames destined to other BSSes
	if (changed_flags & FIF_OTHER_BSS) {
		DEBUG("configure_filter: FIF_OTHER_BSS")
	}

//	pass all multicast frames, this is used if requested by the user or if the hardware is not capable of filtering by multicast address.
//	if (*total_flags & FIF_ALLMULTI || mc_count > 0) {
//		DEBUG("configure_filter: MULTICAST >0 ")
//		priv->rx_conf |= WBUSB_RX_CONF_MULTICAST;
//	}
//	else {
//		DEBUG("configure_filter: MULTICAST 0")
//		priv->rx_conf &= ~WBUSB_RX_CONF_MULTICAST;
//	}
*/
	*total_flags = 0;

/*
	//Acknowledge that we wrote the bit

	if (priv->rx_conf & RTL818X_RX_CONF_FCS)
		*total_flags |= FIF_FCSFAIL;
*/

//	if (priv->rx_conf & WBUSB_RX_CONF_MULTICAST)
//		*total_flags |= FIF_ALLMULTI;

//	Write new config in register
//	wbusb_reg_write(priv, REG_M00, priv->rx_conf);
	return;
}

static const struct ieee80211_ops wbusb_ops = {
	.tx			= wbusb_tx,
	.start			= wbusb_start,
	.stop			= wbusb_stop,
	.add_interface		= wbusb_add_interface,
	.remove_interface	= wbusb_remove_interface,
	.config			= wbusb_config,
	.configure_filter	= wbusb_configure_filter,
	.config_interface	= wbusb_config_interface,
};

//USB device found
static int __devinit wbusb_probe(struct usb_interface *intf,const struct usb_device_id *id_table)
{
	struct usb_device *udev = interface_to_usbdev(intf);
	struct ieee80211_hw *dev;
	struct wbusb_priv *priv;
//	struct ieee80211_channel *channel;
//	u8 perm_addr[ETH_ALEN];

	int err;
	DECLARE_MAC_BUF(mac);

	DEBUG("probe start")
	dev = ieee80211_alloc_hw(sizeof(*priv), &wbusb_ops);
	if (!dev) {
		printk(KERN_ERR "wbusb: ieee80211 alloc failed\n");
		return -ENOMEM;
	}

	priv = dev->priv;
	SET_IEEE80211_DEV(dev, &intf->dev);
	usb_set_intfdata(intf, dev);
	priv->udev = udev;
	usb_get_dev(udev);

	skb_queue_head_init(&priv->rx_queue);

	//Channels and rates
	memcpy(priv->channels, wbusb_channels, sizeof(wbusb_channels));
	memcpy(priv->rates, wbusb_rates, sizeof(wbusb_rates));

	priv->band.band = IEEE80211_BAND_2GHZ;
	priv->band.channels = priv->channels;
	priv->band.n_channels = ARRAY_SIZE(wbusb_channels);
	priv->band.bitrates = priv->rates;
	priv->band.n_bitrates = ARRAY_SIZE(wbusb_rates);
	dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;

	priv->mode = NL80211_IFTYPE_MONITOR;
	dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
		     IEEE80211_HW_RX_INCLUDES_FCS;

//???	dev->extra_tx_headroom = sizeof(struct wbusb_tx_hdr);
	dev->queues = 1;
	dev->max_signal = 64;

	//EP0 regs initial, before any register read/write
	priv->regs.EP0vm_state = VM_STOP;
	priv->regs.EP0vm_count = 0;

	//Set MAC address
	wbusb_get_mac(priv, priv->perm_addr);
	if (!is_valid_ether_addr(priv->perm_addr)) {
		printk(KERN_WARNING "wbusb: Invalid hwaddr! Using random MAC address\n");
		random_ether_addr(priv->perm_addr);
	}
	SET_IEEE80211_PERM_ADDR(dev, priv->perm_addr);

	wbusb_set_mac(priv, priv->perm_addr);

	printk("wbusb: mac address: %s\n", print_mac(mac, dev->wiphy->perm_addr));

	err = ieee80211_register_hw(dev);
	if (err) {
		printk(KERN_ERR "wbusb: Cannot register device\n");
		goto err_free_dev;
	}

	//Initialize radio chip
	err = rf_init(priv);
	//No RF chip found, can not continue
	if (err) goto err_free_dev;

	DEBUG("probe end")
	return 0;

 err_free_dev:
	DEBUG("called err_free_dev")
	ieee80211_free_hw(dev);
	usb_set_intfdata(intf, NULL);
	usb_put_dev(udev);
	return err;
}

//USB device unplugged
static void __devexit wbusb_disconnect(struct usb_interface *intf)
{
	struct ieee80211_hw *dev = usb_get_intfdata(intf);
	struct wbusb_priv *priv;

	DEBUG("disconnect")

	if (!dev) return;

	ieee80211_unregister_hw(dev);

	priv = dev->priv;
	usb_put_dev(interface_to_usbdev(intf));
	ieee80211_free_hw(dev);
}

static struct usb_driver wbusb_driver = {
	.name = "wbusb",
	.probe = wbusb_probe,
	.disconnect = wbusb_disconnect,
	.id_table = Id_Table,
};

int __init wbusb_init(void)
{
	DEBUG("init")
	return usb_register(&wbusb_driver);
}

void __exit wbusb_exit(void)
{
	DEBUG("exit")
	usb_deregister(&wbusb_driver);
}

module_init(wbusb_init);
module_exit(wbusb_exit);
